package com.ikingtech.platform.business.message.service;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fasterxml.jackson.core.type.TypeReference;
import com.ikingtech.framework.sdk.base.model.BatchParam;
import com.ikingtech.framework.sdk.context.exception.FrameworkException;
import com.ikingtech.framework.sdk.context.security.Me;
import com.ikingtech.framework.sdk.enums.message.MessageDeliverStatusEnum;
import com.ikingtech.framework.sdk.enums.message.MessageReadStatusEnum;
import com.ikingtech.framework.sdk.enums.message.MessageReceiverTypeEnum;
import com.ikingtech.framework.sdk.message.model.rpc.MessageSendParam;
import com.ikingtech.framework.sdk.user.api.UserApi;
import com.ikingtech.framework.sdk.user.model.UserBasicDTO;
import com.ikingtech.framework.sdk.utils.Tools;
import com.ikingtech.platform.business.message.entity.*;
import com.ikingtech.platform.business.message.exception.MessageExceptionInfo;
import com.ikingtech.platform.business.message.service.repository.MessageChannelDefinitionRepository;
import com.ikingtech.platform.business.message.service.repository.MessageRepository;
import com.ikingtech.platform.business.message.service.router.MessageRouteProxy;
import com.ikingtech.platform.business.message.service.router.RouteRequest;
import com.ikingtech.platform.business.message.service.router.RouteResult;
import lombok.RequiredArgsConstructor;

import java.time.LocalDateTime;
import java.util.*;

/**
 * @author tie yan
 */
@RequiredArgsConstructor
public class MessageService {

    private final MessageRepository repo;

    private final MessageTemplateService templateService;

    private final MessageChannelDefinitionRepository channelDefinitionService;

    private final MessageReceiverDefinitionService receiverDefinitionService;

    private final MessageParamDefinitionService paramDefinitionService;

    private final MessageRedirectDefinitionService redirectDefinitionService;

    private final UserApi userApi;

    private final MessageRouteProxy routeProxy;

    public void send(MessageSendParam sendParam) {
        //通过消息标识，获取消息模板
        MessageTemplateDO templateEntity = this.templateService.getByMessageTemplateKey(sendParam.getMessageTemplateKey());
        if (null == templateEntity) {
            throw new FrameworkException(MessageExceptionInfo.MESSAGE_TEMPLATE_NOT_FOUND);
        }

        List<MessageDO> entities = new ArrayList<>();

        //通过模板id获取消息通道定义,通道指的是系统消息,钉钉消息,短信等内部或外部系统的通道
        List<MessageChannelDefinitionDO> channelDefinitionEntities = this.channelDefinitionService.list(Wrappers.<MessageChannelDefinitionDO>lambdaQuery().eq(MessageChannelDefinitionDO::getTemplateId, templateEntity.getId()));

        //消息接收者
        List<MessageReceiverDefinitionDO> receiverDefinitionEntities = this.receiverDefinitionService.listByTemplateId(templateEntity.getId());
        //获取到所有的接收人信息
        List<UserBasicDTO> users = this.parseReceiverUser(receiverDefinitionEntities, sendParam.getMessage());

        //获取所有的消息定义,并根据通道分组
        List<MessageParamDefinitionDO> paramDefinitionEntities = this.paramDefinitionService.listByTemplateIdExcludePreDefinition(templateEntity.getId());
        Map<String, List<MessageParamDefinitionDO>> paramDefinitionMap = Tools.Coll.convertGroup(paramDefinitionEntities, MessageParamDefinitionDO::getChannelDefinitionId);

        //获取所有的消息重定向定义,并根据通道分组
        List<MessageRedirectDefinitionDO> redirectDefinitionEntities = this.redirectDefinitionService.listByTemplateId(templateEntity.getId());
        Map<String, List<MessageRedirectDefinitionDO>> redirectDefinitionMap = Tools.Coll.convertGroup(redirectDefinitionEntities, MessageRedirectDefinitionDO::getChannelDefinitionId);

        channelDefinitionEntities.forEach(channelDefinitionEntity ->
                users.forEach(user -> {
            //根据通道获取消息路由代理,并执行route方法构造及发送消息
            RouteResult result = this.routeProxy.determine(channelDefinitionEntity.getChannel())
                    .route(RouteRequest.builder()
                            .businessKey(templateEntity.getBusinessKey())
                            .channelId(channelDefinitionEntity.getChannelId())
                            .channelTemplateId(channelDefinitionEntity.getChannelTemplateId())
                            .templateContent(channelDefinitionEntity.getContent())
                            .showNotification(channelDefinitionEntity.getShowNotification())
                            .paramMap(Tools.Coll.convertMap(paramDefinitionMap.get(channelDefinitionEntity.getId()), MessageParamDefinitionDO::getParamName, paramDefinitionEntity -> Tools.Str.isBlank(paramDefinitionEntity.getMappedParamName()) ? Tools.Str.EMPTY : paramDefinitionEntity.getMappedParamName()))
                            .redirect(Tools.Coll.convertMap(redirectDefinitionMap.get(channelDefinitionEntity.getId()), MessageRedirectDefinitionDO::getRedirectName, MessageRedirectDefinitionDO::getRedirectTo))
                            .target(user)
                            .messageWrapper(sendParam.getMessage())
                            .build());
            MessageDO entity = new MessageDO();
            entity.setId(Tools.Id.uuid());
            entity.setMessageContent(result.getContent());
            entity.setRedirectTo(Tools.Coll.join(result.getRedirectTo()));
            entity.setCause(result.getCause());
            entity.setDeliverStatus((result.getSuccess() != null && result.getSuccess()) ? MessageDeliverStatusEnum.DELIVERED.name() : MessageDeliverStatusEnum.DELIVER_FAILED.name());
            entity.setReadStatus(MessageReadStatusEnum.NO_READ.name());
            entity.setTemplateId(templateEntity.getId());
            entity.setBusinessName(templateEntity.getBusinessName());
            entity.setBusinessKey(templateEntity.getBusinessKey());
            entity.setMessageTemplateKey(templateEntity.getMessageTemplateKey());
            entity.setMessageTitle(templateEntity.getMessageTemplateTitle());
            entity.setChannel(channelDefinitionEntity.getChannel());
            entity.setDeliverTime(LocalDateTime.now());
            entity.setReceiverId(user.getId());
            entity.setTenantCode(Tools.Str.isBlank(sendParam.getTenantCode()) ? Me.tenantCode() : sendParam.getTenantCode());
            entities.add(entity);
        }));
        //记录发送的消息
        if (Tools.Coll.isNotBlank(entities)) {
            this.repo.saveBatch(entities);
        }
    }

    private List<UserBasicDTO> parseReceiverUser(List<MessageReceiverDefinitionDO> receiverDefinitionEntities, Object messageWrapper) {
        Map<String, Object> messageParamMap = Tools.Json.objToMap(messageWrapper);
        Map<String, List<String>> receiverTypeMap = new HashMap<>();

        receiverDefinitionEntities.forEach(entity -> {
            //从消息体中根据接收方参数名称获取接收人员ids
            List<String> receiverIds = this.parseReceiverIdFromParam(messageParamMap, entity.getReceiverParamName());
            if (Tools.Coll.isNotBlank(receiverIds)) {
                //转为map,map的key是接收者类型,值是接收人员ids
                receiverTypeMap.computeIfAbsent(entity.getReceiverType(), k -> new ArrayList<>()).addAll(receiverIds);
            }
        });

        List<UserBasicDTO> users = new ArrayList<>();
        receiverTypeMap.forEach((receiverType, receiverIds) -> {
            //根据接收者类型和接收人员ids获取用户信息
            if (MessageReceiverTypeEnum.USER.name().equals(receiverType)) {
                users.addAll(this.userApi.listInfoByIds(BatchParam.build(receiverIds)).getData());
            }
            if (MessageReceiverTypeEnum.MENU.name().equals(receiverType)) {
                users.addAll(this.userApi.listInfoByMenuCodes(BatchParam.build(receiverIds)).getData());
            }
            if (MessageReceiverTypeEnum.ROLE.name().equals(receiverType)) {
                users.addAll(this.userApi.listInfoByRoleIds(BatchParam.build(receiverIds)).getData());
            }
            if (MessageReceiverTypeEnum.POST.name().equals(receiverType)) {
                users.addAll(this.userApi.listInfoByPostIds(BatchParam.build(receiverIds)).getData());
            }
            if (MessageReceiverTypeEnum.POST.name().equals(receiverType)) {
                users.addAll(this.userApi.listInfoByPostIds(BatchParam.build(receiverIds)).getData());
            }
        });
        return users;
    }

    private List<String> parseReceiverIdFromParam(Map<String, Object> messageParamMap, String paramName) {
        Object paramValue = messageParamMap.get(paramName);
        if (null == paramValue) {
            return new ArrayList<>();
        }
        if (paramValue instanceof List) {
            return Tools.Json.objToBean(messageParamMap.get(paramName), new TypeReference<>() {
            });
        }
        if (paramValue instanceof String) {
            return Collections.singletonList((String) paramValue);
        }
        return new ArrayList<>();
    }
}
